import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Queue;
import java.util.LinkedList;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.cli.ParseException;

/**
 * Iscas 85 Parser
 * Lexical, Syntatic and Semantic Parser.
 * Can be executed on its own for testing purposes.
 *
 */
public class Iscas85Parser {
	/**
	 * Generates an "Options" object to parse
	 * the command line options
	 * 
	 * @return Options object with the options information
	 */
	private static Options buildOptions() {
		Options options = new Options();

		// Dotty file output
		Option dotty = new Option("d", "dotty", true,
				"Outputs a dotty representation of the circuit");
		dotty.setArgName("output file");
		dotty.setOptionalArg(false);
		dotty.setRequired(false);

		// Output dotty with simulation values
		Option dValues = new Option(
				"v",
				"values",
				false,
				"Outputs a dotty representation of the circuit with the values resulting from a simulation");
		dotty.setOptionalArg(false);
		dotty.setRequired(false);

		// Simulate circuit
		Option simulate = new Option("s", "simulate", true,
				"Simulates circuit usage");
		simulate.setArgName("input file");
		simulate.setOptionalArg(false);
		simulate.setRequired(false);

		// Output circuit result
		Option output = new Option("o", "output", true,
				"Outputs results of circuit simulation (default result.out)");
		output.setArgName("output file");
		output.setOptionalArg(true);
		output.setRequired(false);

		// Help
		Option help = new Option("h", "help", false, "Prints help information");
		help.setOptionalArg(false);
		help.setRequired(false);
		
		// Generate sample input file
		Option generate = new Option("g", "generate", true, 
		"Generates a skeleton input file for the built circuit(default input.in)");
		generate.setArgName("input file");
		generate.setOptionalArg(true);
		generate.setRequired(false);

		// Add options
		options.addOption(dotty);
		options.addOption(dValues);
		options.addOption(simulate);
		options.addOption(output);
		options.addOption(help);
		options.addOption(generate);

		return options;
	}

	/**
	 * Parses the arguments passed by command line
	 * 
	 * @param args
	 * 			Arguments (as recieved from main)
	 * @return CommandLine object (stores the arguments passed)
	 */
	private static CommandLine parseArguments(String[] args) {
		Options options = buildOptions();
		PosixParser parser = new PosixParser();
		CommandLine cmd = null;

		try {
			cmd = parser.parse(options, args);

			// Help
			if (cmd.hasOption("h")) {
				new HelpFormatter()
						.printHelp(
								"iscas85sim file [options ...]",
								"Simulator for Iscas85 circuits. Circuits are loaded from netlists. Possibility to output dotty files.",
								options, "");
				return null;
			}

			// Input file check
			String[] files = cmd.getArgs();
			if (files.length != 1) {
				System.out
						.println("Arguments error: exactly one circuit netlist required");
				return null;
			}
			try {
				File file = new File(files[0]);
				if (!file.exists()) {
					System.out
							.println("Arguments error: netlist file does not exist");
					return null;
				}
			} catch (Exception e) {
				System.out
						.println("Arguments error: problem opening netlist file");
				return null;
			}

			// Simulation check
			if (cmd.hasOption("s")) {
				try {
					File file = new File(cmd.getOptionValue("s"));
					if (!file.exists()) {
						System.out
								.println("Arguments error: input values file does not exist");
						return null;
					}
				} catch (Exception e) {
					System.out
							.println("Arguments error: problem opening input values file");
					return null;
				}
			} else if (cmd.hasOption("o")) {
				System.out
						.println("Arguments error: can't output results without a simulation");
				return null;
			}
			
			if (cmd.hasOption("v") && (!cmd.hasOption("s") || !cmd.hasOption("d"))) {
				System.out
						.println("Arguments error: can't append simulation results without simulating the circuit and generating a dotty file");
				return null;
			}
		} catch (ParseException exp) {
			System.out.println("Arguments error: " + exp.getMessage());
			return null;
		}

		return cmd;
	}
	/**
	 * Simulates the parsed circuit
	 * @param cmd
	 * 			CommandLine object with the arguments passed
	 * @param graphBuilder
	 * 			GraphBuilder with the graph information (generated by the parser)
	 */
	private static void simulateCircuit(CommandLine cmd, GraphBuilder graphBuilder) {
		Iscas85Simulator sim = new Iscas85Simulator(graphBuilder);

		// Parses input file
		if (!sim.loadInputValues(cmd.getOptionValue("s")))
			return;

		// Simulates a circuit run and outputs the results as specified
		sim.simulate();
		if (cmd.hasOption("o")) {
			String file = cmd.getOptionValue("o");
			if (file == null)
				file = "result.out";
			sim.outputResults(file, cmd.getArgs()[0]);
		} else
			sim.printResults();
	}

	/**
	 * Main function (executes the compiler)
	 * @param args
	 * 			Arguments passed
	 */
	@SuppressWarnings("static-access")
	public static void main(String[] args) {
		CommandLine cmd = parseArguments(args);
		if (cmd == null)
			return;

		// Parse netlist
		SimpleNode root = null;
		try {
			root = new NetlistParser(new FileInputStream(cmd.getArgs()[0]))
					.InitParse();
		} catch (FileNotFoundException e) {
			System.out.println("Arguments error: " + e.getMessage());
			return;
		} catch (Exception e) {
			System.out.println("Parsing error: " + e.getMessage());
			return;
		}

		// Semantic analysis
		SemanticParser semParser;
		try {
			semParser = new SemanticParser(root);
		} catch (Exception e1) {
			System.out.println(e1.getMessage());
			return;
		}
		if (!semParser.checkSemantic())
			return;

		// Building circuit
		if (!semParser.buildGraph()) {
			return;
		}
		GraphBuilder graphBuilder = semParser.graphBuilder;
		
		// Generate input file if needed
		if(cmd.hasOption("g")){
			String input_file = cmd.getOptionValue("g");
			if(input_file == null)
				input_file = "input.in";		
			try{
				FileWriter outFile = new FileWriter(input_file);
				PrintWriter out = new PrintWriter(outFile);
				String error = "0.0", fill = "0";
				out.println("### File generated automatically ###");
				out.println();
				out.println(error+":f"+fill);
				for(Iscas85Node node : graphBuilder.inputNodes) {
					out.println(node.name+":"+ fill);
				}
				out.close();
				System.out.println("File generated successfully");
			}catch(Exception e){
				System.out.println("Problem creating file");
			}
		}
		
		// Simulate if needed
		if (cmd.hasOption("s"))
			simulateCircuit(cmd, graphBuilder);

		// Output dotty if requested
		if (cmd.hasOption("d"))
			try {
				graphBuilder.exportDotFile(cmd.getOptionValue("d"),
						"iscas85circuit", cmd.hasOption("v"));
			} catch (IOException e) {
				System.out.println("Could not create dotty file");
				return;
			}
	}

}
